package me.boot.db.mybatis.config;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.*;
import lombok.extern.slf4j.Slf4j;
import me.boot.db.mybatis.handler.BaseEnumTypeHandler;
import me.boot.db.mybatis.interceptor.ForeignKeyInterceptor;
import me.boot.db.mybatis.interceptor.TableNameInterceptor;
import me.boot.db.mybatis.interceptor.datascope.DataScopeInterceptor;
import me.boot.db.mybatis.interceptor.foreign.ForeignKeyService;
import me.boot.db.mybatis.interceptor.foreign.impl.ForeignKeyServiceImpl;
import me.boot.db.mybatis.interceptor.tenant.TenantColumnModeInterceptor;
import me.boot.db.mybatis.interceptor.tenant.TenantSchemaModeInterceptor;
import me.boot.db.mybatis.propertites.BasicDataSourceProperties;
import me.boot.db.mybatis.propertites.DbProperties;
import me.boot.db.mybatis.propertites.TenantMode;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.List;

@Slf4j
@ComponentScan("me.boot.db.mybatis")
@MapperScan("**.mapper.**")
@EnableConfigurationProperties({BasicDataSourceProperties.class, DbProperties.class})
@Configuration(proxyBeanMethods = false)
public class MybatisPlusConfiguration {

    @Autowired
    private DbProperties dbProperties;

    @Autowired
    ForeignKeyService foreignKeyService;

    @PostConstruct
    void init() {
        log.info("Init MybatisPlusConfiguration");
    }

    /**
     * 分页插件
     * <p>
     * 注意:
     * 如果内部插件都是使用,需要注意顺序关系,建议使用如下顺序
     * 多租户插件,动态表名插件
     * 分页插件,乐观锁插件
     * sql性能规范插件,防止全表更新与删除插件
     * 总结: 对sql进行单次改造的优先放入,不对sql进行改造的最后放入
     * <p>
     */
    @Bean("mybatisPlusInterceptor")
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        //租户插件
        TenantMode mode = dbProperties.getTenant().getMode();
        if (StrUtil.equalsAnyIgnoreCase(mode.name(), TenantMode.COLUMN.name(), TenantMode.SCHEMA.name())) {
            log.info("租户模式:{}", mode);
            if (mode.equals(TenantMode.COLUMN)) {
                interceptor.addInnerInterceptor(new TenantColumnModeInterceptor(dbProperties.getTenant().getColumn()));
            }
            if (mode.equals(TenantMode.SCHEMA)) {
                interceptor.addInnerInterceptor(new TenantSchemaModeInterceptor(dbProperties.getTenant().getSchema()));
            }
        }

        //数据权限插件
        if (dbProperties.isDataScope()) {
            interceptor.addInnerInterceptor(new DataScopeInterceptor());
        }

        //外键检查插件
        if (dbProperties.getForeignKey().isEnable()) {
            interceptor.addInnerInterceptor(new ForeignKeyInterceptor(foreignKeyService));
        }

        //动态表名插件
        if (dbProperties.getDynamicTable().isEnable()) {
            interceptor.addInnerInterceptor(new TableNameInterceptor(dbProperties.getDynamicTable()));
        }

        //分页插件之前，添加扩展插件
        List<InnerInterceptor> beforeInnerInterceptor = getPaginationBeforeInnerInterceptor();
        if (!beforeInnerInterceptor.isEmpty()) {
            beforeInnerInterceptor.forEach(interceptor::addInnerInterceptor);
        }

        //分页插件
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        paginationInnerInterceptor.setMaxLimit(dbProperties.getPageMaxLimit());
        paginationInnerInterceptor.setOverflow(true);
        paginationInnerInterceptor.setDbType(dbProperties.getDbType());
        interceptor.addInnerInterceptor(paginationInnerInterceptor);

        //分页插件之后，添加扩展插件
        List<InnerInterceptor> afterInnerInterceptor = getPaginationAfterInnerInterceptor();
        if (!afterInnerInterceptor.isEmpty()) {
            afterInnerInterceptor.forEach(interceptor::addInnerInterceptor);
        }

        //乐观锁插件
        if (dbProperties.isOptimisticLocker()) {
            interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        }

        //sql性能规范 , 谨慎使用
        if (dbProperties.isIllegalSql()) {
            interceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor());
        }

        //防止全表更新与删除插件:
        if (dbProperties.isBlockAttack()) {
            interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        }
        return interceptor;
    }

    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> {
            //自定义枚举类型解析
            configuration.setDefaultEnumTypeHandler(BaseEnumTypeHandler.class);
        };
    }

    @Bean
    @ConditionalOnProperty(value = "db.foreignKey.enable", havingValue = "true", matchIfMissing = true)
    public ForeignKeyService foreignKeyService() {
        return new ForeignKeyServiceImpl(dbProperties.getForeignKey().getEntityPackages());
    }

    /**
     * 分页拦截器之后的插件
     *
     * @return 分页拦截器之后的插件
     */
    protected List<InnerInterceptor> getPaginationAfterInnerInterceptor() {
        return Collections.emptyList();
    }

    /**
     * 分页拦截器之前的插件
     *
     * @return 分页拦截器之前的插件
     */
    protected List<InnerInterceptor> getPaginationBeforeInnerInterceptor() {
        return Collections.emptyList();
    }

}
